﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;

namespace Skynet.CommonLibrary.Extension
{
   public static   class DelegateExtension
    {
        /// <summary>
        /// 获取带参数的方法
        /// </summary>
        /// <param name="type"></param>
        /// <param name="methodName"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        public static MethodInfo GetMethodInfoOrParams(this Type type, string methodName, object[] values)
        {
            List<Type> typeList = new List<Type>();
            if (values != null)
            {
                typeList.AddRange(values.Select(o => o.GetType()));
            }
            return type.GetMethod(methodName,
                BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase,
                null,
                typeList.Count > 0 ? typeList.ToArray() : null,
                null);
        }

        //动态生成自定义委托 供Delegate.CreateDelegate 调用
        public static Type CreateCustomDelegate(this MethodInfo targetMethod)
        {
            AssemblyName assembly = new AssemblyName
                                        {
                                            Version = new Version(1, 0, 0, 0), 
                                            Name = "CallbackGen"
                                        };
            AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assembly, AssemblyBuilderAccess.RunAndSave);
            ModuleBuilder modbuilder = assemblyBuilder.DefineDynamicModule("CallbackGenModule", "CallbackGen.dll", true);
            // Create a delegate that has the same signature as the method we would like to hook up to
            TypeBuilder typeBuilder = modbuilder.DefineType(targetMethod.Name + "Callback", TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed |
                                                                                            TypeAttributes.AnsiClass | TypeAttributes.AutoClass, typeof(MulticastDelegate));
            ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.RTSpecialName | MethodAttributes.HideBySig |
    MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(object), typeof(int) });
            constructorBuilder.SetImplementationFlags(MethodImplAttributes.CodeTypeMask);
            // Grab the parameters of the method
            ParameterInfo[] parameters = targetMethod.GetParameters();
            Type[] paramTypes = new Type[parameters.Length];
            for (int i = 0; i < parameters.Length; i++)
            {
                paramTypes[i] = parameters[i].ParameterType;
            }
            // Define the Invoke method for the delegate
            MethodBuilder methodBuilder = typeBuilder.DefineMethod(
                "Invoke",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual,
                CallingConventions.Standard,
                targetMethod.ReturnType,
                null,
                new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) },
                paramTypes,
                null,
                null);
            methodBuilder.SetImplementationFlags(MethodImplAttributes.CodeTypeMask);
            // bake it!
            Type t = typeBuilder.CreateType();
            return t;
        }
    }
}
